const fs = require('fs');
const path = require('path');
const postcss = require('postcss');
const babel = require('babel-core');
const traverse = require('babel-traverse').default;
const generate = require('babel-generator').default;

const pathExt = require('./path-ext');

exports.rename = (source, dest, refs, opts) => {
  refs.forEach(item => {
    let extname = path.extname(item);
    switch (extname) {
      case '.js':
        jsRefHandler(item, source, dest, opts.jsConfig);
        break;
      case '.scss':
      case '.less':
        styleRefHandler(item, source, dest, opts.styleConfig);
        break;
      case '.vue':
        vueRefHandler(item, source, dest, opts);
        break;
    }
  });
  fs.renameSync(source, dest);
}

function vueRefHandler (filename, source, dest, opts = {}) {
  let code = fs.readFileSync(filename, 'utf-8');
  let _code;

  let scriptCode = code.match(/<script>([\s\S]*)<\/script>/) ? RegExp.$1 : '';
  let jsAst = _jsRefHandler(filename, scriptCode, source, dest, opts.jsConfig);
  _code = code.replace(/<script>([\s\S]*)<\/script>/g, generate(jsAst).code);
  fs.writeFileSync(filename, _code, 'utf-8');

  let styleCode = code.match(/<style>([\s\S]*)<\/style>/) ? RegExp.$1 : '';
  let styleRoot = _styleRefHandler(filename, styleCode, source, dest, opts.styleConfig);
  _code = code.replace(/<style>([\s\S]*)<\/style>/g, styleRoot.toString());
  fs.writeFileSync(filename, _code, 'utf-8');
}

function jsRefHandler (filename, source, dest, config = {}) {
  let code = fs.readFileSync(filename, 'utf-8');
  let ast = _jsRefHandler(filename, code, source, dest, config);
  fs.writeFileSync(filename, generate(ast).code, 'utf-8');
}

function _jsRefHandler (filename, code = '', source, dest, config = {}) {
  let parser = babel.transform(code);
  traverse(parser.ast, {
    enter (_path) {
      let result;
      let astValue;
      if (_path.node.type === 'ImportDeclaration') {
        astValue = _path.node.source.value;
        result = pathExt.parsePath(source, astValue, config);
        if (result === source) {
          _path.node.source.value = getDestValue(filename, astValue, dest, config);
        }
      }

      if (_path.node.type === 'CallExpression' && _path.node.callee.name === 'require') {
        astValue = _path.node.arguments[0].value;
        result = pathExt.parsePath(source, astValue, config);
        if (result === source) {
          _path.node.arguments[0].value = getDestValue(filename, astValue, dest, config);
        }
      }
    }
  });
  return parser.ast;
}

function styleRefHandler (filename, source, dest, config = {}) {
  let code = fs.readFileSync(filename, 'utf-8');
  let root = _styleRefHandler(filename, code, source, dest, config);
  fs.writeFileSync(filename, root.toString(), 'utf-8');
}

function _styleRefHandler (filename, code = '', source, dest, config = {}) {
  let root = postcss.parse(code);
  root.walkAtRules(rule => {
    if (rule.name !== 'import') {
      return;
    }
    let basename = path.basename(filename);
    let importPath = path.resolve(basename, rule.params);
    if (importPath !== source) {
      return;
    }
    rule.params = getDestValue(filename, rule.params, dest, config);
  });
  return root;
}

function getDestValue (filename, astValue, dest, config) {
  let destValue;
  let alias;
  let pathAlias = config.pathAlias || {};
  if (pathExt.isRelativePath(astValue)) {
    let _relative = path.relative(path.dirname(filename), path.dirname(dest)) || '.';
    destValue = `${_relative}/${path.basename(dest)}`;
  } else if (alias = pathExt.getAliasPath(astValue, pathAlias)) {
    destValue = `${alias.key}/${path.relative(alias.value, dest)}`;
  }
  return destValue;
}
